package com.wan.paypal.v2;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import com.paypal.http.HttpResponse;
import com.paypal.http.serializer.Json;
import com.paypal.orders.*;
import com.paypal.orders.LinkDescription;
import com.paypal.orders.Money;
import com.paypal.payments.*;
import com.paypal.payments.Refund;
import org.apache.commons.lang3.StringUtils;
import org.json.JSONObject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import lombok.extern.slf4j.Slf4j;

@Slf4j
@Service
public class  PayPalCheckoutService {

    @Value("${paypal.receiver.email}")
    private String receiverEmail;
    @Autowired
    PayPalConfig config;

    /**
     * IPN异步回调
     */
    public String callback(@SuppressWarnings("rawtypes") Map map) {
        log.info(map.toString());
        String outTradeNo = (String)map.get("invoice");
        String paymentStatus = (String)map.get("payment_status");
        String amount = (String)map.get("mc_gross");
        String currency = (String)map.get("mc_currency");
        String paymentId = (String)map.get("txn_id");
        String parentPaymentId = (String)map.get("parent_txn_id");
        log.info("商家订单号 = {}", outTradeNo);
        log.info("订单状态 = {}", paymentStatus);
        log.info("金额 = {}", amount);
        log.info("币种 = {}", currency);
        log.info("流水号 = {}", paymentId);
        log.info("父流水号 = {}", parentPaymentId);

        if (!receiverEmail.equals((String) map.get("receiver_email"))) {
            log.info("FAIL = 商户id错误, outTradeNo = {}", outTradeNo);
            return "failure";
        }
        if("Completed".equals(paymentStatus)) {
            //进行数据库操作
            //
            //
            log.info("支付成功,状态为=COMPLETED");
            return "success";
        }
        if("Refunded".equals(paymentStatus)) {
            //进行数据库操作
            //
            //
            log.info("退款成功");
            return "success";
        }
        if("Pending".equals(paymentStatus) && StringUtils.isEmpty(parentPaymentId)) {
            String pendingReason = String.valueOf(map.get("pending_reason"));
            //进行数据库操作
            //
            //
            log.info("订单支付成功,状态为=PENDING，产生此状态的原因是 {}", pendingReason );
            return "success";
        }
        if(StringUtils.isEmpty(parentPaymentId)) {
            if(PayPalCheckoutConstant.PAYMENT_STATUS_REVERSED.equals(paymentStatus)
                    || PayPalCheckoutConstant.PAYMENT_STATUS_CANCELED_REVERSAL.equals(paymentStatus)
                    || PayPalCheckoutConstant.PAYMENT_STATUS_DENIED.equals(paymentStatus)) {
                String reasonCode = String.valueOf(map.get("reason_code"));
                //进行数据库操作(状态修改)
                //
                //
                log.info("订单异常,请尽快查看处理，状态为={}，产生此状态的原因是 {} ", paymentStatus, reasonCode);
                return PayPalCheckoutConstant.SUCCESS;
            }
            if(PayPalCheckoutConstant.PAYMENT_STATUS_EXPIRED.equals(paymentStatus)
                    || PayPalCheckoutConstant.PAYMENT_STATUS_CREATED.equals(paymentStatus)
                    || PayPalCheckoutConstant.PAYMENT_STATUS_FAILED.equals(paymentStatus)
                    || PayPalCheckoutConstant.PAYMENT_STATUS_PROCESSED.equals(paymentStatus)
                    || PayPalCheckoutConstant.PAYMENT_STATUS_VOIDED.equals(paymentStatus)) {
                //进行数据库操作(状态修改)
                //
                //
                log.info("其他订单状态,订单异常,请尽快查看处理， 状态={}", paymentStatus);
                return PayPalCheckoutConstant.SUCCESS;
            }
        }
        return "failure";
    }

    /**
     * 创建退款请求体
     */
    public RefundRequest buildRefundRequestBody() {
        RefundRequest refundRequest = new RefundRequest();
        com.paypal.payments.Money money = new com.paypal.payments.Money();

        money.currencyCode("USD");
        money.value("40.00");
        refundRequest.amount(money);
        refundRequest.invoiceId("T202005230002");
        refundRequest.noteToPayer("7天无理由退款");
        return refundRequest;
    }

    /**
     * 申请退款
     */
    public HttpResponse<Refund> refundOrder(String orderNo) throws IOException {

        OrdersGetRequest ordersGetRequest = new OrdersGetRequest(orderNo);
        HttpResponse<com.paypal.orders.Order> ordersGetResponse = null;
        try {
            ordersGetResponse = config.client().execute(ordersGetRequest);
        } catch (Exception e) {
            try {
                log.error("第1次调用paypal订单查询失败");
                ordersGetResponse = config.client().execute(ordersGetRequest);
            } catch (Exception e2) {
                try {
                    log.error("第2次调用paypal订单查询失败");
                    ordersGetResponse = config.client().execute(ordersGetRequest);
                } catch (Exception e3) {
                    log.error("第3次调用paypal订单查询失败，失败原因：{}", e3.getMessage());
                }
            }
        }
        String captureId = ordersGetResponse.result().purchaseUnits().get(0).payments().captures().get(0).id();
        CapturesRefundRequest request = new CapturesRefundRequest(captureId);
        request.prefer("return=representation");
        request.requestBody(buildRefundRequestBody());
        HttpResponse<Refund> response = null;
        try {
            response = config.client().execute(request);
        } catch (IOException e) {
            try {
                log.error("第1次调用paypal退款申请失败");
                response = config.client().execute(request);
            } catch (Exception e1) {
                try {
                    log.error("第2次调用paypal退款申请失败");
                    response = config.client().execute(request);
                } catch (Exception e2) {
                    log.error("第3次调用paypal退款申请失败，失败原因 {}", e2.getMessage());
                }
            }
        }
        log.info("Status Code = {}, Status = {}, RefundID = {}", response.statusCode(), response.result().status(), response.result().id());
        if("COMPLETED".equals(response.result().status())) {
            //进行数据库操作，修改状态为已退款（配合回调和退款查询确定退款成功）
            log.info("退款成功");
        }
        for (com.paypal.payments.LinkDescription link : response.result().links()) {
            log.info("Links-{}: {}    \tCall Type: {}", link.rel(), link.href(), link.method());
        }
        String json = new JSONObject(new Json().serialize(response.result())).toString(4);
        log.info("refundOrder response body: {}", json);
        return response;
    }

    /**
     * service：生成订单主体信息
     */
    public OrderRequest buildCreateRequestBody(String local_order_no) {
        OrderRequest orderRequest = new OrderRequest();
        orderRequest.checkoutPaymentIntent(PayPalCheckoutConstant.AUTHORIZE);

        ApplicationContext applicationContext = new ApplicationContext()
                .brandName(PayPalCheckoutConstant.BRANDNAME)
                .landingPage(PayPalCheckoutConstant.LANDINGPAGE)
                .cancelUrl(config.getDomain() + PayPalCheckoutConstant.CANCEL_URL + "?local_order_no=" + local_order_no)
                .returnUrl(config.getDomain() + PayPalCheckoutConstant.RETURN_URL)
                .shippingPreference(PayPalCheckoutConstant.SHIPPINGPREFERENCE)
                .userAction(PayPalCheckoutConstant.USERACTION);
        orderRequest.applicationContext(applicationContext);

        List<PurchaseUnitRequest> purchaseUnitRequests = new ArrayList<PurchaseUnitRequest>();
        PurchaseUnitRequest purchaseUnitRequest = new PurchaseUnitRequest()
                .description("新一代读写一体，智能电子笔记本")
                .customId(local_order_no)
                .invoiceId(local_order_no)
                .amountWithBreakdown(new AmountWithBreakdown()
                        .currencyCode("USD")
                        .value("219.00")// value = itemTotal + shipping + handling + taxTotal + shippingDiscount;
                        .amountBreakdown(new AmountBreakdown()
                                .itemTotal(new com.paypal.orders.Money().currencyCode("USD").value("220.00")) // itemTotal = Item[Supernote A6](value × quantity) + Item[帆布封套](value × quantity)
                                .taxTotal(new com.paypal.orders.Money().currencyCode("USD").value("1.00"))
                                .shipping(new com.paypal.orders.Money().currencyCode("USD").value("2.00"))
                                .handling(new com.paypal.orders.Money().currencyCode("USD").value("3.00"))
                                .insurance(new com.paypal.orders.Money().currencyCode("USD").value("4.00"))
                                .shippingDiscount(new com.paypal.orders.Money().currencyCode("USD").value("5.00"))
                                .discount(new com.paypal.orders.Money().currencyCode("USD").value("6.00"))))
                .items(new ArrayList<Item>() {
                    {
                        add(new Item().name("Supernote A6").description("丝滑般流畅的书写体验")
                                .unitAmount(new com.paypal.orders.Money()
                                        .currencyCode("USD")
                                        .value("200.00"))
                                .quantity("1"));
                        add(new Item().name("帆布封套").description("黑色帆布保护封套")
                                .unitAmount(new com.paypal.orders.Money()
                                        .currencyCode("USD")
                                        .value("20.00"))
                                .quantity("1"));
                    }
                })
                .shippingDetail(new ShippingDetail()
                        .name(new Name().fullName("RATTA"))
                        .addressPortable(new AddressPortable()
                                .addressLine1("梅陇镇")
                                .addressLine2("集心路168号")
                                .adminArea2("闵行区")
                                .adminArea1("上海市")
                                .postalCode("20000")
                                .countryCode("CN")));
        purchaseUnitRequests.add(purchaseUnitRequest);
        orderRequest.purchaseUnits(purchaseUnitRequests);

        return orderRequest;
    }

    /**
     * service：执行扣款
     *      orderId：第三方订单号
     *      authroizeId：第三方交易流水号
     */
    public HttpResponse<Order> authorizeOrder(String orderId) throws IOException {
        OrdersAuthorizeRequest request = new OrdersAuthorizeRequest(orderId);
        request.requestBody(new OrderRequest());
        HttpResponse<Order> response = null;
        try {
            response = config.client().execute(request);
        } catch (IOException e1) {
            try {
                log.error("第1次调用paypal扣款失败");
                response = config.client().execute(request);
            } catch (Exception e) {
                try {
                    log.error("第2次调用paypal扣款失败");
                    response = config.client().execute(request);
                } catch (Exception e2) {
                    log.error("第3次调用paypal扣款失败，失败原因 {}", e2.getMessage() );
                }
            }
        }
        log.info("Status Code = {}, Status = {}, OrderID = {}", response.statusCode(), response.result().status(), response.result().id());
        for (LinkDescription link : response.result().links()) {
            log.info("Links-{}: {}    \tCall Type: {}", link.rel(), link.href(), link.method());
        }
        if ("COMPLETED".equals(response.result().status())){
            for (PurchaseUnit purchaseUnit : response.result().purchaseUnits()) {
                for (com.paypal.orders.Authorization authorization : purchaseUnit.payments().authorizations()) {
                    log.info("已授权，待领取");
                    log.info("authorize id: {}", authorization.id());
                    log.info("status: {}", authorization.status());
                    log.info("invoice_id: {}", authorization.invoiceId());
                }
            }
        }
        Payer buyer = response.result().payer();
        log.info("Buyer Email Address: {}", buyer.email());
        log.info("Buyer Name: {} {}", buyer.name().givenName(), buyer.name().surname());
        String json = new JSONObject(new Json().serialize(response.result())).toString(4);
        //log.info("captureOrder response body: {}", json);
        return response;
    }

    /**
     * service：获取授权订单信息（获取已付款交易详情）
     */
    public String getAuthorizeInfo (String transactionNo) throws IOException {

        AuthorizationsGetRequest request = new AuthorizationsGetRequest(transactionNo);
        HttpResponse<com.paypal.payments.Authorization> response = config.client().execute(request);

        System.out.println("Status Code: " + response.statusCode());
        System.out.println("Status: " + response.result().status());
        System.out.println("Capture ids: " + response.result().id());
        System.out.println("Links: ");
        for (com.paypal.payments.LinkDescription link : response.result().links()) {
            System.out.println("\t" + link.rel() + ": " + link.href() + "\tCall Type: " + link.method());
        }
        System.out.println("Full response body:");
        System.out.println(new JSONObject(new Json().serialize(response.result())).toString(4));

        return response.result().status();
    }

    /**
     * service：查询支付单详情
     */
    public HttpResponse<Order> getOrdersInfo(String orderNo) throws IOException {
        OrdersGetRequest request = new OrdersGetRequest(orderNo);
        HttpResponse<Order> response = null;
        try {
            response = config.client().execute(request);
        } catch (Exception e) {
            try {
                System.out.println("调用paypal订单查询失败，链接异常1");
                response = config.client().execute(request);
            } catch (Exception e2) {
                try {
                    System.out.println("调用paypal订单查询失败，链接异常2");
                    response = config.client().execute(request);
                } catch (Exception e3) {
                    System.out.println("调用paypal订单查询失败，链接异常3");
                    System.out.println(e3.getMessage());
                }
            }
        }
        return response;
    }
}